package com.tbit.utils;

import com.tbit.config.payConfig.WxMchConfig;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Random;

/**
 * 微信V3工具类
 *
 * @author <a href="mailto:zhang.zhiliang@tbit.com.cn">Zhang ZhiLiang</a>
 * @version 1.0
 * @see <a href="http://adsl1.tbit.com.cn:21490/pages/viewpage.action?pageId=82182491">订单号规范</a>
 * @since 2024/6/3
 */
public class WxV3Utils {

    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private static final Random RANDOM = new SecureRandom();

    /**
     * 生成微信V3签名
     * @param httpMethod    请求方式 GET，POST 等
     * @param url   请求微信的uri
     * @param timestamp     请求时间戳
     * @param nonceStr      随机字符串
     * @param body  请求体参数
     * @param apiClientKeyPath  微信V3s私钥路径（apiclient_key.pem）
     * @return
     * @throws Exception
     */
    public static String generateSignature(String httpMethod, String url, String timestamp, String nonceStr, String body, String apiClientKeyPath) throws Exception {
        // 构建签名字符串
        String signString = String.format("%s\n%s\n%s\n%s\n%s\n", httpMethod, url, timestamp, nonceStr, body);

        // 获取私钥
        PrivateKey privateKey = getPrivateKey(apiClientKeyPath);

        // 使用 SHA256WithRSA 进行签名
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(signString.getBytes(StandardCharsets.UTF_8));

        // 生成签名并进行 Base64 编码
        byte[] signBytes = signature.sign();
        return Base64.getEncoder().encodeToString(signBytes);
    }


    /**
     * 获取微信V3私钥
     *
     * @param filePath 微信V3s私钥路径（apiclient_key.pem）
     * @return 私钥对象
     */
    private static PrivateKey getPrivateKey(String filePath) throws IOException {

        String content = new String(Files.readAllBytes(Paths.get(filePath)), "utf-8");
        try {
            String privateKeyStr = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");

            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyStr)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }


    /**
     * 获取公钥证书
     *
     * @param inputStream 证书文件的文件流 (apiclient_cert.pem)
     * @return {@link X509Certificate} 获取证书
     */
    private static X509Certificate getCertificate(InputStream inputStream) {
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
            cert.checkValidity();
            return cert;
        } catch (CertificateExpiredException e) {
            throw new RuntimeException("证书已过期", e);
        } catch (CertificateNotYetValidException e) {
            throw new RuntimeException("证书尚未生效", e);
        } catch (CertificateException e) {
            throw new RuntimeException("无效的证书", e);
        }
    }


    /**
     * 获取长度为32的随机字符串
     * @return
     */
    public static String generateNonceStr() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        }
        return new String(nonceChars);
    }

    /**
     * 微信V3加密，RSA公钥加密算法
     * 开发者应当使用微信支付平台证书中的公钥，对上送的敏感信息进行加密
     * @param message   待加密信息
     * @param config    微信V3配置对象
     * @return
     * @throws IllegalBlockSizeException
     * @throws IOException
     */
    public static String rsaEncryptOAEP(String message, WxMchConfig config)
            throws IllegalBlockSizeException, IOException {
        X509Certificate certificate = getCertificate(Files.newInputStream(Paths.get(config.getPrivateCertPath())));
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());
            byte[] data = message.getBytes("utf-8");
            byte[] cipherdata = cipher.doFinal(data);
            return Base64.getEncoder().encodeToString(cipherdata);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("无效的证书", e);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
        }
    }

    /**
     * 微信V3解密信息
     * 微信支付使用商户API证书中的公钥对下行的敏感信息进行加密。开发者应使用商户私钥对下行的敏感信息的密文进行解密。
     * @param ciphertext
     * @param config    微信V3配置对象
     * @return
     * @throws BadPaddingException
     * @throws IOException
     */
    public static String rsaDecryptOAEP(String ciphertext, WxMchConfig config)
            throws BadPaddingException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
            PrivateKey privateKey = getPrivateKey(config.getPrivateKeyPath());
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] data = Base64.getDecoder().decode(ciphertext);
            return new String(cipher.doFinal(data), "utf-8");
        } catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("无效的私钥", e);
        } catch (BadPaddingException | IllegalBlockSizeException e) {
            throw new BadPaddingException("解密失败");
        }
    }
}
